package org.novaacropole.util;

import java.lang.reflect.Field;
import java.sql.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;

public class DAO {

	private static ThreadLocal<EntityManager> threadLocal = new ThreadLocal<EntityManager>();
	private static EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("pegasus");

	static {
		BeanUtilsBean.getInstance().getConvertUtils().register(null, Date.class);
	}

	private static EntityManager get() {
		EntityManager entityManager = threadLocal.get();
		if (entityManager == null) {
			entityManager = entityManagerFactory.createEntityManager();
			threadLocal.set(entityManager);
		}
		return entityManager;
	}

	public static void begin() {
		get().getTransaction().begin();
	}

	public static void commit() {
		get().getTransaction().commit();
	}

	public static void rollback() {
		if (get().getTransaction().isActive())
			get().getTransaction().rollback();
	}

	public static void close() {
		if (get().isOpen())
			get().close();
		threadLocal.remove();
	}

	public static void save(Object entity) {
		Object id = getId(entity);
		if (id != null)
			mergeLikePersist(entity);
		else
			get().persist(entity);
		flush();

	}

	private static void flush() {
		get().flush();
	}

	private static Object getId(Object entity) {
		EntityType<?> entityType = get().getMetamodel().entity(entity.getClass());
		Class<?> idClass = entityType.getIdType().getJavaType();
		SingularAttribute<?, ?> singularAttribute = entityType.getDeclaredId(idClass);
		Field field = (Field) singularAttribute.getJavaMember();
		try {
			return field.get(entity);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private static void mergeLikePersist(Object entity) {
		try {
			BeanUtils.copyProperties(entity, get().merge(entity));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static void remove(Object entity) {
		get().createQuery("delete from " + entity.getClass().getName() + " entity where entity = :entity").setParameter("entity", entity)
				.executeUpdate();
		flush();
	}

	public static List<?> list(String hql, Object... params) {
		return fillParams(hql, params).getResultList();
	}

	public static Object single(String hql, Object... params) {
		try {
			return fillParams(hql, params).getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	private static Query fillParams(String hql, Object... param) {
		Query query = get().createQuery(hql);
		//((QueryImpl<?>) query).getHibernateQuery().setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		for (int i = 0; i < param.length; i = i + 2)
			query.setParameter((String) param[i], param[i + 1]);
		return query;
	}

}
